定制模块

  1. 对于定制的 IDE 产品,基于 Theia 应该是最多的。这几年 OpenSumi 非常火,它比 Theia 的定制成本更加低,性能更加好。业界基于 VS Code 魔改最成功的产品严格意义上只有 Azure 的 DataStudio,Code Server 和 Gitpod 对于源码定制部分其实不多。但是我们却选择了 VS Code,一方面是我们最终克服了对于修改 VS Code 源码的恐惧,做到了很少有人做的事情。其次是我们认为我们还是走在对的路上的,我们认为在我们这种玩法下, MC/EMR/SQL 这一类的特定引擎语言 IDE 想要落地,其实成本也没有那么高了。
  2. 我们走通了一条路: 从开源中来,再回馈到开源社区。

难点

1. 深入定制的难点

1. 深入定制没有文档

深入 UI 定制

比如我们在最开始觉得直接使用原生的 DOM 开发 UI (在底座中)是一种极其低效的方式,所以做过两种尝试:

  1. 想通过在扩展中透传 React 组件的信息过来,然后在底座中进行渲染,但是这种方式在实现上有很多的问题,比如:扩展与底座的交互,可以通过 SDK 进行数据交换,但是也仅限于可以进行序列化和反序列化的对象、字符串。 我最开始天真的觉得,将 React 的虚拟 DOM 对象传入,在底座中引入 ReactDOM 直接就可以渲染。实际上,React 虚拟节点中携带类似 $$xxx 的属性,经过 SDK 传参之后丢失,底座根本拿不到一个正常可渲染的节点。这样的想法,是出于对架构的不了解:
    1. 就算在底座中填补上虚拟节点的 $$xxx 的属性,还是无法保证在通信过程中是否有其他属性会丢失,导致渲染节点失败。
    2. 后来了解了 VS Code 与 Extension 之间的架构之后,了解到实际上扩展与底座之间是通过 RPC 通信,字符串可以正常传,对象被序列化之后传,而函数,也需要特殊处理,只有 $ 开头的函数,在主进程和扩展进程中才可以互相调用,普通函数会被过滤。
  2. 我想到的第二种方式,是直接在 VS Code Web 中引入 React + 组件库,画出一个 UI 页面来(当然这种方式当初也只是为了 demo)
  3. 第二步中遇到了一个棘手的问题,直接渲染 DOM 节点的 UI 无法正常滚动。后面研究了编辑器、文件树、编辑器顶部 Tab(可以滑动的模块)
    1. 为什么无法正常滚动? VS Code 整个模块全部禁用掉了上下左右的默认的滚动事件,稍微旧一点的版本 WebView 的左右滚动事件没有禁用,因此在 Webview 上可以左右后退(是一个 bug)。取而代之的是,VS Code 自行在 DOM 实现的事件监听。
    2. 那么要实现一个滚动模块要怎么做?
      1. 可滚动的 DOM:决定可滚动区域的高度,以及滚动高度,需要根据这两者渲染右侧滚动条的高度。
      2. 可滚动的对象: 决定当前的列表滚动到哪里了。

除此之外,我们还定制了编辑器顶部工具栏,右侧面板的 Panel 层。全都是按照 VS Code 的规范来玩的,但这些都没有透出文档,只能通过阅读源码来实现。

遇到这样的问题,前期比较痛苦,我们会以传统前端的思维去猜测我们不了解的功能是如何实现的,而且对于 VS Code 模块划分、DI 注入的这种方式(其实不能很好的通过 VS Code 原生的跳转,因为大量的 interface 影响跳转)

2. 构建与传统前端的差异

通过 gulp 构建,底座不是通过 Webpack 构建的。

  1. 云构建卡顿,很慢。 一次构建需要 20 分钟(本地构建 8 - 10 分钟,其余都是安装依赖的事件)左右。
  2. 样式隔离: VS Code 与扩展的 Webview 样式是隔离的,但是与业务 (公共头)默认在一张 html 里,样式会互相影响。
  3. 国际化: 底座、内建扩展、普通扩展国际化包的方式差异很大,需要将业务的国际化状态与 VS Code 底座进行同步。
    1. 底座默认开发环境只能英文,中文需要在构建过程中将国际化包准备好,再最后一步构建时处理。
    2. 内建扩展的国际化需要 gallery 支持,国际化包将 bundle 和 package 包统一提供了,但是 package 数据需要请求后端接口,再 replace 掉才能生效。
    3. 普通扩展的 package 和 bundle 文件全部由业务扩展自己提供。
  4. 部署方式的问题。
    1. 部门部署前端方式的问题,不能满足 Web Worker 的同域保证

3. 定期合并开源版本代码

  1. 使用开源软件有一个问题是,保证安全一般都会锁版本,但是锁版本了,就无法享受到新版本的功能,并且也可能会存在致命缺陷,如果很久没有更新,合并的时候就会很痛苦;但是如果不锁版本(合并代码比较频繁),那需要非常完善的测试机制,保证合并之后的产品质量。并且,需要将合并的时机,与我们的产品的发布时机契合的比较好。目前合并的时机策略:
    1. 定期(2-3 个月)合并一次
    2. 或者是有重大的 bug 修复的时候合并一次。
    3. 每月 VS Code 的版本更新日志我们会看,并在周会上给团队的同学介绍,如果有比较吸引我们的功能,我们也会主动求合入。
  2. 合并过程比较头大。 很多模块被我们修改了之后,可能新版本已经删除了,或者是修改了,我们需要重新修改。
    1. code 平台。。。 站点都无法显示一次 diff。代码量很大(通常是一两个月更新一次),几千个文件,几万行代码改动很正常。CR 的网站打开都很卡顿。
    2. 更新底座代码时,源码架构变化,文件移动、重命名等,代码合并的困难。 需要人工 recheck, 重新 review 功能。
  3. 合并之后的工程质量保证
    1. 合并代码并没有办法很好的解决。 但是对于功能的保证,可以通过单元测试、集成测试来保证改动不影响功能。分析了 VS Code 现有的测试方式: 单元测试、集成测试、扩展测试、冒烟测试。 我们按照他们的规范,写我们业务自己的单元测试和集成测试代码。
    2. CI 不能直接跑,也需要人工在本地执行。集团内的基建问题,Aone 一直没有自动执行的测试脚本的功能,所以我们只能暂时手动执行。持续集成的问题,我们在着手解决。

4. 克制

经过接近 2 年时间的摸索,其实对于目前的我来说,基本上对于底座的任何改动我都有信心可以按照 VS Code 的玩法进行魔改。但是,这样的改动,对于其他人来说,是不是也能够很好的接受呢?这是一个问题。在迁移新版数据开发的过程中,我们遇到很多比较模凌两可的交互,深入底座来修改也可以,由扩展来改变交互也可以。

例如,我们最近遇到一个问题,提出 sql 包的概念,将代码和配置文件统一成一个软件包,类似 jar 包,这一层会作为逻辑层,与传统的文件树有一定的差异,功能少了很多。

结果

  1. 核心产品灰度内测中;某产品接入之后,提升 xxx
  2. 扩展工程化支持,支持 x 扩展的工程,内、外上线。
  3. copilot 研究,支持 lsp 编辑器落地
Last Updated:
Contributors: yiliang114